import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:sqflite/orm/data_class.dart';

import '../sqflite_dev.dart';

class DataClassNameGenerator extends GeneratorForAnnotation<DataClassName> {

  final StringBuffer contentBuffer = StringBuffer();

  @override
  generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) async {
    if(element is! ClassElement) {
      throw InvalidGenerationSourceError("@DataClassName can only be applied to class");
    }
    String className = element.displayName;
    // String path = buildStep.inputId.path;
    String path = buildStep.allowedOutputs.first.path;
    print("====================Clas Generator: $className $path\n");

    contentBuffer.clear();
    Iterable<Element> children = element.fields.where((element) {
      final type = element.type.toString();
      return type.startsWith(RegExp(r"Column<\w+>"));
    });
    _generateDataClass(children, element, annotation, buildStep);
    _generateTable(children, element, annotation, buildStep);
    String s = contentBuffer.toString();

    //使用自定义的路径存储数据
    await buildTempFile(s, buildStep);
    // print(s);
    return null;
  }

  _generateDataClass(Iterable<Element> children, Element element, ConstantReader annotation, BuildStep buildStep) async {
    List<_Column> list = children.map((e) => _spanForElement(e)).toList();

    String className = annotation.read('name').stringValue;
    contentBuffer.writeln("class $className extends DataClass {");
    //生成属性字段
    for (var ele in list) {
      contentBuffer.writeln('final ${ele.type}? ${ele.displayName};');
    }
    contentBuffer.writeln();

    //生成构造函数
    contentBuffer.writeln("$className ({");
    for (var ele in list) {
      contentBuffer.writeln('this.${ele.displayName},');
    }
    contentBuffer.writeln("});");
    contentBuffer.writeln();

    //生成 toJson
    contentBuffer.writeln("Map<String, dynamic> toJson() {");
    contentBuffer.writeln("return <String, dynamic>{");
    for (var ele in list) {
      contentBuffer.writeln('\'${ele.displayName}\': ${ele.displayName},');
    }
    contentBuffer.writeln("};");
    contentBuffer.writeln("}");
    contentBuffer.writeln();

    //生成 toColumns
    contentBuffer.writeln("///将当前类的数据转换为可存储于数据库中的数据");
    contentBuffer.writeln("Map<String, dynamic> toColumns() {");
    contentBuffer.writeln("return <String, dynamic>{");
    for (var ele in list) {
      contentBuffer.writeln('\'${ele.tableName}\': toTable<${ele.type}?>(${ele.displayName}),');
    }
    contentBuffer.writeln("};");
    contentBuffer.writeln("}");
    contentBuffer.writeln();

    //生成 fromColumns
    contentBuffer.writeln("///将数据库表的数据解析为当前类的数据");
    contentBuffer.writeln("factory $className.fromColumns(Map<String, dynamic> json) {");
    contentBuffer.writeln("return $className(");
    for (var ele in list) {
      contentBuffer.writeln('${ele.displayName}: DataClass.fromTable<${ele.type}?>(json[\'${ele.tableName}\']),');
    }
    contentBuffer.writeln(");");
    contentBuffer.writeln("}");
    contentBuffer.writeln();

    //生成 ==函数
    contentBuffer.writeln("@override");
    contentBuffer.writeln("bool operator ==(Object other) =>");
    contentBuffer.writeln("identical(this, other) ||");
    contentBuffer.writeln("(other is $className");
    for (var ele in list) {
      contentBuffer.write(' &&');
      contentBuffer.writeln('other.${ele.displayName} == ${ele.displayName}');
    }
    contentBuffer.writeln(");");
    contentBuffer.writeln();

    //生成 hashCode
    contentBuffer.writeln("@override");
    contentBuffer.writeln("int get hashCode => Object.hashAll(${list.map((e) => e.displayName).toList()});");
    contentBuffer.writeln();

    //生成 toString
    contentBuffer.writeln("@override");
    contentBuffer.writeln("String toString() {");
    contentBuffer.writeln("return (StringBuffer('$className(')");
    for(int i = 0, size = list.length; i < size; i++) {
      var ele = list[i];
      if(i == size - 1) { //最后一个不需要逗号
        contentBuffer.writeln('..write(\'${ele.displayName}: \$${ele.displayName}\')');
      } else {
        contentBuffer.writeln('..write(\'${ele.displayName}: \$${ele.displayName}, \')');
      }
    }
    contentBuffer.writeln('..write(\')\'))');
    contentBuffer.writeln('.toString();');
    contentBuffer.writeln("}");
    contentBuffer.writeln();

    contentBuffer.writeln("}");
  }

  ///扩展表结构
  _generateTable(Iterable<Element> children, Element element, ConstantReader annotation, BuildStep buildStep) {
    List<String> fields = children.map((e) => e.displayName).toList();
    contentBuffer.writeln("class \$${element.displayName} extends ${element.displayName} {");
    contentBuffer.writeln("@override");
    contentBuffer.writeln("List<Column> get columns => $fields;");
    contentBuffer.writeln("}");
  }

  ///查找该字段的完整定义
  _Column _spanForElement(Element element) {
    final source = element.source?.contents.data??'';
    if(element is FieldElement) {
      int s = element.getter!.nameOffset + element.getter!.nameLength;
      int start = source.indexOf("=>", s);
      int end = source.indexOf(";", s);
      final location = source.substring(start + 3, end).trim();
      // print("loc:$location,ele:$element");
      return _Column(location, element);
    }
    throw InvalidGenerationSourceError("@DataClassName field $element error");
  }

}

class _Column {
  late String tableName;
  late String type;
  final Element element;

  _Column(String string, this.element) {
    int i = string.indexOf('named(');
    int j = string.indexOf(')', i);
    tableName = string.substring(i + 7,j - 1);

    if(string.startsWith('text()')) {
      type = 'String';
    } else if(string.startsWith('integer()')) {
      type = 'int';
    } else if(string.startsWith('dateTime()')) {
      type = 'DateTime';
    } else if(string.startsWith('int64()')) {
      type = 'BigInt';
    } else if(string.startsWith('boolean()')) {
      type = 'bool';
    } else if(string.startsWith('blob()')) {
      type = 'Uint8List';
    } else if(string.startsWith('real()')) {
      type = 'double';
    } else {
      throw InvalidGenerationSourceError("@DataClassName field $element error, type $string");
    }
  }

  String get displayName => element.displayName;
}